﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Jacaranda.Model
{
    public class EntityMetadata
    {
        public EntityMetadata()
        {
            Associations = new EntityAssociation[] {};
            Fields = new FieldMetadata[] {};
            AssociationFields = new AssociationField[] {};
            SubTypes = new EntityMetadata[] {};
        }

        private Type type;
        public Type Type
        {
            get { return type; }
            set
            {
                type = value;

                if (type.GetCustomAttributes(typeof(BaseClassAttribute), false).Length == 1)
                {
                    IsTable = false;
                }
                else
                {
                    IsTable = true;
                }

                TableName = GetTableName(type);
                DisplayName = GetDisplayName(type);
                Create = GetCreate(type);
            }
        }


        public EntityMetadata[] InheritanceChain
        {
            get
            {
                List<EntityMetadata> entities = new List<EntityMetadata>();

                EntityMetadata current = this;

                while (current != null)
                {
                    entities.Add(current);
                    current = current.BaseType;
                }

                return entities.ToArray();
            }
        }

        public EntityMetadata[] RelatedEntities
        {
            get
            {
                if (IsTable)
                {
                    return new EntityMetadata[] { this };
                }
                else
                {
                    return SubTypes.ToArray();
                }
            }
        }

        public EntityMetadata BaseType { get; internal set; }

        public EntityMetadata[] SubTypes { get; internal set; }

        public bool IsTable { get; private set; }

        public bool Create { get; private set; }

        public string TableName { get; private set; }

        public string DisplayName { get; private set; }

        public EntityAssociation[] Associations { get; internal set; }

        public FieldMetadata[] Fields { get; internal set; }

        public AssociationField[] AssociationFields { get; internal set; }

        public FieldMetadata PrimaryKey
        {
            get { return Fields.FirstOrDefault(i => i.IsPrimaryKey); }
        }

        public FieldMetadata DbPrimaryKey
        {
            get { return Fields.FirstOrDefault(i => i.IsDbPrimaryKey); }
        }

        internal object GetValueOfPrimaryKey(Entity entity)
        {
            FieldMetadata field = PrimaryKey;
            return (field == null) ? null : field.GetValue(entity);
        }


        private static string GetTableName(Type type)
        {
            TableAttribute tableAttribute = type.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault(i => i is TableAttribute) as TableAttribute;
            if (tableAttribute != null && !string.IsNullOrEmpty(tableAttribute.Name))
            {
                return tableAttribute.Name;
            }
            else
            {
                return type.Name;
            }
        }

        internal static string GetDisplayName(Type type)
        {
            DisplayAsAttribute displayAsAttribute = type.GetCustomAttributes(typeof(DisplayAsAttribute), true).FirstOrDefault(i => i is DisplayAsAttribute) as DisplayAsAttribute;
            if (displayAsAttribute != null && !string.IsNullOrEmpty(displayAsAttribute.Name))
            {
                return displayAsAttribute.Name;
            }
            else
            {
                return type.Name;
            }
        }

        internal static bool GetCreate(Type type)
        {
            TableAttribute tableAttribute = type.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault(i => i is TableAttribute) as TableAttribute;
            return tableAttribute != null && tableAttribute.Create;
        }

        internal AssociationField FindAssociationField(string propertyName)
        {
            EntityMetadata current = this;

            while (current != null)
            {
                AssociationField field = current.AssociationFields.FirstOrDefault(i => i.PropertyName == propertyName);
                if (field != null)
                {
                    return field;
                }

                current = current.BaseType;
            }

            return null;
        }
    }
}
